package ph.com.gs3.formalistics.presenter;

import java.util.ArrayList;
import java.util.List;

import org.json.JSONException;

import ph.com.gs3.formalistics.R;
import ph.com.gs3.formalistics.global.constants.ApplicationConfigs;
import ph.com.gs3.formalistics.global.constants.ApplicationConfigs.ApplicationMode;
import ph.com.gs3.formalistics.global.interfaces.CallbackCommand;
import ph.com.gs3.formalistics.global.utilities.logging.FLLogger;
import ph.com.gs3.formalistics.model.communicators.HttpCommunicator;
import ph.com.gs3.formalistics.model.valueobjects.business.User;
import ph.com.gs3.formalistics.model.valueobjects.business.document.Document;
import ph.com.gs3.formalistics.model.valueobjects.business.document.DocumentAction;
import ph.com.gs3.formalistics.model.valueobjects.business.document.DocumentStub;
import ph.com.gs3.formalistics.model.valueobjects.business.document.OutgoingDocument;
import ph.com.gs3.formalistics.model.valueobjects.business.document.Document.DocumentStar;
import ph.com.gs3.formalistics.model.valueobjects.business.form.Form;
import ph.com.gs3.formalistics.model.valueobjects.business.form.FormStub;
import ph.com.gs3.formalistics.presenter.DocumentActivity.DocumentType;
import ph.com.gs3.formalistics.presenter.fragments.views.MainViewFragment;
import ph.com.gs3.formalistics.presenter.fragments.views.MainViewFragment.MainViewActivityActionListener;
import ph.com.gs3.formalistics.presenter.fragments.views.MainViewFragment.NotificationMode;
import ph.com.gs3.formalistics.presenter.fragments.views.MainViewFragment.ViewAddMode;
import ph.com.gs3.formalistics.presenter.fragments.views.MainViewFragment.ViewDataType;
import ph.com.gs3.formalistics.presenter.fragments.workers.MainWorkerFragment;
import ph.com.gs3.formalistics.service.managers.SessionManager;
import ph.com.gs3.formalistics.service.managers.DocumentManager.DocumentSaveFailedException;
import ph.com.gs3.formalistics.service.managers.SessionManager.CheckConnectionResult;
import ph.com.gs3.formalistics.service.managers.SessionManager.Mode;
import ph.com.gs3.formalistics.service.managers.SessionManager.SessionManagerEventListener;
import ph.com.gs3.formalistics.service.services.DocumentsUpdateService;
import ph.com.gs3.formalistics.service.services.FormsUpdateService;
import ph.com.gs3.formalistics.service.services.FullDataUpdateService;
import ph.com.gs3.formalistics.service.services.OutgoingDocumentActionSubmitService;
import ph.com.gs3.formalistics.service.services.OutgoingStarMarksSubmitService;
import ph.com.gs3.formalistics.service.services.FullDataUpdateService.FullDataUpdateListener;
import ph.com.gs3.formalistics.service.services.FullDataUpdateService.FullDataUpdateServiceBinder;
import ph.com.gs3.formalistics.view.dialogs.DocumentActionsDialogFragment;
import ph.com.gs3.formalistics.view.dialogs.FormSelectionDialogFragment;
import ph.com.gs3.formalistics.view.dialogs.FormSelectionDialogFragment.FormsAvailability;
import ph.com.gs3.formalistics.view.valueobjects.UserActionListViewItem;
import ph.com.gs3.formalistics.view.valueobjects.UserActionListViewItem.UpdateType;
import ph.com.gs3.formalistics.view.valueobjects.parsers.DocumentParser;
import ph.com.gs3.formalistics.view.valueobjects.parsers.OutgoingDocumentParser;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.FragmentManager;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.Toast;

/**
 * 
 * @author Ervinne Sodusta
 */
public class MainActivity extends FragmentActivity implements MainViewActivityActionListener {

	public static final String TAG = MainActivity.class.getSimpleName();

	public static final String EXTRA_LOGGED_IN_USER = "user";

	public static final int REQUEST_CODE_WAIT_FOR_LOGOUT = 1;

	// Singletons
	private SessionManager sessionManager = SessionManager.getApplicationInstance();

	// View Fragments & Dialogs
	private MainViewFragment mainViewFragment;
	private FormSelectionDialogFragment formSelectionDialogFragment;

	// Worker Fragments
	private MainWorkerFragment mainWorkerFragment;

	// Data Fields
	private ArrayList<FormStub> currentFormFilterSelection;

	// State Fields
	private boolean fullUpdateServiceBound;
	private boolean fullUpdateCurrentlyRunning;
	private boolean formsAreCurrentlyUpdating;

	// ========================================================================
	// {{ Activity Implementation Methods

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);

		initializeWorkerFragment();
		initializeData();

		registerBroadcastReceivers();
		registerOtherListeners();

		// startFullUpdateService();
		onRefreshDocumentsRequested();

		if (savedInstanceState == null) {
			NotificationMode mainViewFragmentNotificationMode = NotificationMode.NOTIFY_NONE;

			if (sessionManager.getMode() == Mode.OFFLINE) {
				mainViewFragmentNotificationMode = NotificationMode.NOTIFY_OFFLINE;
			} else if (sessionManager.getMode() == Mode.ONLINE) {
				mainViewFragmentNotificationMode = NotificationMode.NOTIFY_ONLINE;
			}

			mainViewFragment = MainViewFragment.createInstance(currentFormFilterSelection,
			        mainViewFragmentNotificationMode);
			getSupportFragmentManager().beginTransaction().add(R.id.container, mainViewFragment)
			        .commit();

			initializeViewData();

		}

	}

	@Override
	public boolean onCreateOptionsMenu(Menu menu) {

		// Inflate the menu; this adds items to the action bar if it is present.

		// if (constants.getApplicationMode() == ApplicationMode.DEVELOPMENT) {
		if (ApplicationConfigs.APPLICATION_MODE == ApplicationMode.DEVELOPMENT) {
			getMenuInflater().inflate(R.menu.main_dev_mode, menu);
		} else if (ApplicationConfigs.APPLICATION_MODE == ApplicationMode.TESTING) {
			getMenuInflater().inflate(R.menu.main_qa_mode, menu);
		} else {
			getMenuInflater().inflate(R.menu.main, menu);
		}

		return true;
	}

	@Override
	public boolean onOptionsItemSelected(MenuItem item) {
		// Handle action bar item clicks here. The action bar will
		// automatically handle clicks on the Home/Up button, so long
		// as you specify a parent activity in AndroidManifest.xml.
		int id = item.getItemId();
		switch (id) {
			case R.id.action_user_profile: {
				User currentUser = mainWorkerFragment.getCurrentlyActiveUser();
				navigateToUserProfileView(currentUser);
			}
				break;
			case R.id.action_open_messenger: {
				User currentUser = mainWorkerFragment.getCurrentlyActiveUser();
				navigateToMessengerView(currentUser);
			}
				break;
			case R.id.action_open_support: {
				User currentUser = mainWorkerFragment.getCurrentlyActiveUser();
				navigateToSupportView(currentUser);
			}
				break;
			case R.id.action_create_document: {
				openSelectFormDialogForDocumentCreation();
			}
				break;
			case R.id.action_open_developer_options: {
				navigateToDeveloperOptions();
			}
				break;
			case R.id.action_logout: {
				mainWorkerFragment.logUserOut();
				returnToLoginActivity();
			}
				break;
		}
		return super.onOptionsItemSelected(item);
	}

	@Override
	public void onActivityResult(int requestCode, int resultCode, Intent data) {

		Bundle extras = null;

		if (data != null) {
			extras = data.getExtras();
		}

		switch (requestCode) {
			case REQUEST_CODE_WAIT_FOR_LOGOUT: {
				if (extras != null) {
					if (extras.getInt(UserProfileActivity.EXTRA_LOGOUT_FLAG) == UserProfileActivity.FLAG_DID_LOGOUT) {
						mainWorkerFragment.logUserOut();
						returnToLoginActivity();
					}
				}
			}
				break;
		}
	}

	@Override
	protected void onDestroy() {
		super.onDestroy();

		unbindServices();
		unregisterBroadcastReceivers();
		unregisterOtherListeners();
	}

	// }}

	// ========================================================================
	// {{ Initialization Methods

	private void initializeData() {

		// data fields
		currentFormFilterSelection = (ArrayList<FormStub>) mainWorkerFragment.getAvailableForms();

		// state fields
		fullUpdateServiceBound = false;

		fullUpdateCurrentlyRunning = false;
		formsAreCurrentlyUpdating = false;
		// documentsAreCurrentlyUpdating = false;
		// commentsAreCurrentlyUpdating = false;

	}

	private void initializeViewData() {

		// passing 0 means no filter
		refreshDocumentsViewData("0");
		refreshOutboxViewData();

	}

	private void refreshDocumentsViewData(String filterFormWebId) {

		List<UserActionListViewItem> allUserActions = new ArrayList<>();
		List<UserActionListViewItem> myWorkUserActions = new ArrayList<>();
		List<UserActionListViewItem> myUserActions = new ArrayList<>();

		List<Document> documents;

		if ("0".equals(filterFormWebId)) {
			// show documents from all forms
			documents = mainWorkerFragment.getAllDocuments();
		} else {
			// show documents only from the specified form
			documents = mainWorkerFragment.getAllDocumentsByForm(filterFormWebId);
		}

		User currentlyActiveUser = mainWorkerFragment.getCurrentlyActiveUser();
		String currentUserWebId = Integer.toString(currentlyActiveUser.getWebId());

		// add the documents to the list

		for (Document document : documents) {

			UserActionListViewItem userListActionListItem = mainWorkerFragment
			        .generateUserActionListItemFromDocument(document, currentlyActiveUser);

			userListActionListItem.updateType = UpdateType.CURRRENT;

			allUserActions.add(userListActionListItem);

			if (currentUserWebId.equals(document.getProcessorWebId())) {
				myWorkUserActions.add(userListActionListItem);
			}

			if (currentUserWebId.equals(document.getRequestorWebId())) {
				myUserActions.add(userListActionListItem);
			}

		}

		mainViewFragment.initializeViewData(allUserActions, ViewDataType.ALL);
		mainViewFragment.initializeViewData(myWorkUserActions, ViewDataType.MY_WORK);
		mainViewFragment.initializeViewData(myUserActions, ViewDataType.MY_ACTIONS);

		refreshStarredDocumentsViewData(filterFormWebId);

	}

	private void refreshStarredDocumentsViewData(String filterFormWebId) {

		List<UserActionListViewItem> myStarredUserActions = new ArrayList<>();

		List<Document> documents;

		if ("0".equals(filterFormWebId)) {
			// show documents from all forms
			documents = mainWorkerFragment.getAllDocuments();
		} else {
			// show documents only from the specified form
			documents = mainWorkerFragment.getAllDocumentsByForm(filterFormWebId);
		}

		User currentlyActiveUser = mainWorkerFragment.getCurrentlyActiveUser();

		// add the documents to the list
		for (Document document : documents) {

			UserActionListViewItem userListActionListItem = mainWorkerFragment
			        .generateUserActionListItemFromDocument(document, currentlyActiveUser);

			userListActionListItem.updateType = UpdateType.CURRRENT;

			if (document.isStarred()) {
				myStarredUserActions.add(userListActionListItem);
			}

		}

		mainViewFragment.initializeViewData(myStarredUserActions, ViewDataType.STARRED);

	}

	private void refreshOutboxViewData() {

		User currentlyActiveUser = mainWorkerFragment.getCurrentlyActiveUser();
		String userDbId = Integer.toString(currentlyActiveUser.getId());
		String server = currentlyActiveUser.getAccountServer();

		List<OutgoingDocument> outgoingDocuments = mainWorkerFragment
		        .getAllOutgoingDocumentActions(userDbId);
		List<Document> documentsWithOutgoingComments = mainWorkerFragment
		        .getAllDocumentsWithOutgoingComments(userDbId, server);

		List<UserActionListViewItem> outgoingDocumentActions = new ArrayList<>();

		// add the outgoing documents to the list
		for (OutgoingDocument outgoingDocument : outgoingDocuments) {

			String outgoingDocumentServer = outgoingDocument.getServer();
			String formWebId = outgoingDocument.getDocumentWebId();

			// This method uses caching so don't worry about that anymore
			Form form = mainWorkerFragment.getForm(outgoingDocumentServer, formWebId);

			UserActionListViewItem userListActionListItem = OutgoingDocumentParser
			        .toUserActionListItem(form, outgoingDocument, currentlyActiveUser);

			userListActionListItem.isOutgoing = true;

			outgoingDocumentActions.add(userListActionListItem);

		}

		// add the documents with outgoing comments to the list
		for (Document documentsWithOutgoingComment : documentsWithOutgoingComments) {

			// This method uses caching so don't worry about that anymore
			Form form = mainWorkerFragment.getForm(server,
			        documentsWithOutgoingComment.getFormWebId());

			UserActionListViewItem userListActionListItem = DocumentParser.toUserActionListItem(
			        documentsWithOutgoingComment, form, currentlyActiveUser, currentlyActiveUser);
			userListActionListItem.footerBody = "Outgoing comment(s)";
			userListActionListItem.isOutgoing = false;

			outgoingDocumentActions.add(userListActionListItem);

		}

		mainViewFragment.initializeViewData(outgoingDocumentActions, ViewDataType.OUTBOX);

	}

	private void initializeWorkerFragment() {
		FragmentManager fragmentMan = getSupportFragmentManager();

		mainWorkerFragment = (MainWorkerFragment) fragmentMan
		        .findFragmentByTag(MainWorkerFragment.TAG);

		if (mainWorkerFragment == null) {
			mainWorkerFragment = MainWorkerFragment.createInstance();
			fragmentMan.beginTransaction().add(mainWorkerFragment, MainWorkerFragment.TAG).commit();
			fragmentMan.executePendingTransactions();
		}
	}

	// }}

	// ========================================================================
	// {{ Functional Methods

	private void unbindServices() {
		if (fullUpdateServiceBound) {
			unbindService(fullDataUpdateServiceConnection);
			fullUpdateServiceBound = false;
			fullUpdateCurrentlyRunning = false;
			FLLogger.d(TAG, "FullDataUpdateService unbound");
		}
	}

	private void registerBroadcastReceivers() {

		// register forms update broadcast receiver

		IntentFilter formsUpdateBroadcastReceiverFilter = new IntentFilter();

		formsUpdateBroadcastReceiverFilter.addAction(FormsUpdateService.ACTION_ON_UPDATE_STARTED);
		formsUpdateBroadcastReceiverFilter.addAction(FormsUpdateService.ACTION_ON_UPDATE_DONE);
		formsUpdateBroadcastReceiverFilter.addAction(FormsUpdateService.ACTION_ON_UPDATE_ERROR);
		formsUpdateBroadcastReceiverFilter.addCategory(Intent.CATEGORY_DEFAULT);
		registerReceiver(formsUpdateBroadcastReceiver, formsUpdateBroadcastReceiverFilter);

		// register documents update broadcast receiver

		IntentFilter documentsUpdateBroadcastReceiverFilter = new IntentFilter();

		// @formatter:off
		documentsUpdateBroadcastReceiverFilter.addAction(DocumentsUpdateService.ACTION_ON_UPDATE_STARTED);
		documentsUpdateBroadcastReceiverFilter.addAction(DocumentsUpdateService.ACTION_ON_DOCUMENT_UPDATED);
		documentsUpdateBroadcastReceiverFilter.addAction(DocumentsUpdateService.ACTION_ON_DOCUMENT_UPDATE_ERROR);
		documentsUpdateBroadcastReceiverFilter.addAction(DocumentsUpdateService.ACTION_ON_UPDATE_DONE);
		documentsUpdateBroadcastReceiverFilter.addCategory(Intent.CATEGORY_DEFAULT);
		// @formatter:on

		registerReceiver(documentsUpdateBroadcastReceiver, documentsUpdateBroadcastReceiverFilter);

		// register mark document star broadcast receiver

		IntentFilter documentStarBroadcastReceiverFilter = new IntentFilter();

		// @formatter:off
		documentStarBroadcastReceiverFilter.addAction(OutgoingStarMarksSubmitService.ACTION_ON_SERVICE_STARTED);
		documentStarBroadcastReceiverFilter.addAction(OutgoingStarMarksSubmitService.ACTION_ON_SERVICE_DONE);
		documentStarBroadcastReceiverFilter.addCategory(Intent.CATEGORY_DEFAULT);
		// @formatter:on

		registerReceiver(markDocumentStarBroadcastReceiver, documentStarBroadcastReceiverFilter);

		// register outgoing document action submit broadcast receiver

		IntentFilter outgoingDocumentActionSubmitBroadcastReceiverFilter = new IntentFilter();

		// @formatter:off
		outgoingDocumentActionSubmitBroadcastReceiverFilter.addAction(OutgoingDocumentActionSubmitService.ACTION_ON_SERVICE_STARTED_SUBMITTING);
		outgoingDocumentActionSubmitBroadcastReceiverFilter.addAction(OutgoingDocumentActionSubmitService.ACTION_ON_EXISTING_DOCUMENT_ACTION_SUBMITTED);
		outgoingDocumentActionSubmitBroadcastReceiverFilter.addAction(OutgoingDocumentActionSubmitService.ACTION_ON_NEW_DOCUMENT_SUBMITTED);
		outgoingDocumentActionSubmitBroadcastReceiverFilter.addAction(OutgoingDocumentActionSubmitService.ACTION_ON_SERVICE_DONE);

		registerReceiver(outgoingDocumentActionSubmitBroadcastReceiver, outgoingDocumentActionSubmitBroadcastReceiverFilter);
		// @formatter:on

	}

	private void unregisterBroadcastReceivers() {

		unregisterReceiver(formsUpdateBroadcastReceiver);
		unregisterReceiver(documentsUpdateBroadcastReceiver);
		unregisterReceiver(markDocumentStarBroadcastReceiver);
		unregisterReceiver(outgoingDocumentActionSubmitBroadcastReceiver);

	}

	private void startFullUpdateService() {

		Intent intent = new Intent(MainActivity.this, FullDataUpdateService.class);

		User currentlyActiveUser = mainWorkerFragment.getCurrentlyActiveUser();
		intent.putExtra(FullDataUpdateService.EXTRA_CURRENT_USER, currentlyActiveUser);

		fullUpdateServiceBound = bindService(intent, fullDataUpdateServiceConnection,
		        Context.BIND_AUTO_CREATE);
		startService(intent);
		FLLogger.d(TAG, "FullUpdateService started");

	}

	private void startDocumentsUpdateService() {

		Intent documentsServiceIntent = new Intent(MainActivity.this, DocumentsUpdateService.class);

		User currentlyActiveUser = mainWorkerFragment.getCurrentlyActiveUser();

		documentsServiceIntent.putExtra(DocumentsUpdateService.EXTRA_CURRENT_USER,
		        currentlyActiveUser);

		startService(documentsServiceIntent);
	}

	private void startMarkDocumentStarService(String formWebId, String documentWebId,
	        DocumentStar star) {

		Intent markDocumentStarService = new Intent(MainActivity.this,
		        OutgoingStarMarksSubmitService.class);

		User currentlyActiveUser = mainWorkerFragment.getCurrentlyActiveUser();
		markDocumentStarService.putExtra(OutgoingStarMarksSubmitService.EXTRA_CURRENT_USER,
		        currentlyActiveUser);
		startService(markDocumentStarService);

	}

	private void submitDocumentAction(Document document, String action) {

		try {
			mainWorkerFragment.saveDocumentAction(document, action);

			User currentUser = mainWorkerFragment.getCurrentlyActiveUser();

			// TODO: add broadcast listeners

			Intent outgoingDocumentsActionSubmitServiceIntent = new Intent(MainActivity.this,
			        OutgoingDocumentActionSubmitService.class);

			outgoingDocumentsActionSubmitServiceIntent.putExtra(
			        OutgoingDocumentActionSubmitService.EXTRA_CURRENT_USER, currentUser);

			startService(outgoingDocumentsActionSubmitServiceIntent);

			Toast.makeText(MainActivity.this,
			        "Action sent, your document will be updated after a while.", Toast.LENGTH_LONG)
			        .show();

			// automatically refresh the outbox
			refreshOutboxViewData();

		} catch (DocumentSaveFailedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();

			Toast.makeText(MainActivity.this,
			        "Unable to send action: " + action + " as of the moment. Try again later.",
			        Toast.LENGTH_LONG).show();
		}

	}

	private void registerOtherListeners() {

		sessionManager.addListener(sessionManagerEventListener);

	}

	private void unregisterOtherListeners() {

		sessionManager.removeListener(sessionManagerEventListener);

	}

	// }}

	// ========================================================================
	// {{ View & Navigation Methods

	private void navigateToUserProfileView(User user) {

		Intent intent = new Intent(MainActivity.this, UserProfileActivity.class);
		intent.putExtra(UserProfileActivity.EXTRA_USER, user);
		startActivityForResult(intent, REQUEST_CODE_WAIT_FOR_LOGOUT);

	}

	private void navigateToMessengerView(User currentUser) {

		Intent intent = new Intent(MainActivity.this, MessengerActivity.class);
		intent.putExtra(MessengerActivity.EXTRA_CURRENT_USER, currentUser);
		startActivity(intent);

	}

	private void navigateToSupportView(User currentUser) {

		Intent intent = new Intent(MainActivity.this, SupportActivity.class);
		intent.putExtra(SupportActivity.EXTRA_CURRENT_USER, currentUser);
		startActivity(intent);

	}

	private void returnToLoginActivity() {

		Intent loginActivity = new Intent(MainActivity.this, LoginActivity.class);
		startActivity(loginActivity);
		finish();

	}

	private void navigateToDeveloperOptions() {

		Intent developerOptionsActivity = new Intent(MainActivity.this,
		        DeveloperOptionsActivity.class);
		startActivity(developerOptionsActivity);

	}

	private void showActionsDialogForDocument(String server, String documentWebId,
	        String documentFormWebId) {

		DocumentActionsDialogFragment dialog = new DocumentActionsDialogFragment();
		List<DocumentAction> actions;

		final Document document;
		try {
			document = mainWorkerFragment.getDocument(documentWebId, documentFormWebId, server);
			actions = document.getAvailableActions();
			dialog.setActions(actions);
			dialog.show(getFragmentManager(), DocumentActionsDialogFragment.TAG);
			dialog.setOnActionSelectedCallback(new CallbackCommand<DocumentAction>() {

				@Override
				public void execute(DocumentAction clickedAction) {
					submitDocumentAction(document, clickedAction.getLabel());
				}
			});
		} catch (JSONException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

	}

	private void openSelectFormDialogForDocumentCreation() {

		if (formSelectionDialogFragment == null) {
			// lazy initialize the dialog fragment
			formSelectionDialogFragment = new FormSelectionDialogFragment();
		}

		if (formsAreCurrentlyUpdating) {
			// forms are still updating
			formSelectionDialogFragment.setFormsAvailability(FormsAvailability.CURRENTLY_UPDATING);
		} else {

			// update the form filter selection
			currentFormFilterSelection = (ArrayList<FormStub>) mainWorkerFragment
			        .getAvailableForms();

			if (currentFormFilterSelection.size() >= 1) {
				formSelectionDialogFragment.setFormsAvailability(FormsAvailability.HAS_FORMS);
			} else {
				formSelectionDialogFragment
				        .setFormsAvailability(FormsAvailability.NO_FORMS_TO_SHOW);
			}

		}

		formSelectionDialogFragment.setFormStubs(currentFormFilterSelection);
		formSelectionDialogFragment.show(getFragmentManager(), FormSelectionDialogFragment.TAG);

		formSelectionDialogFragment.setOnFormSelectedCallback(new CallbackCommand<FormStub>() {

			@Override
			public void execute(FormStub selectedForm) {
				navigateToCreateDocumentView(selectedForm.dbId);
			}
		});

	}

	private void navigateToCreateDocumentView(String formId) {

		User currentlyActiveUser = mainWorkerFragment.getCurrentlyActiveUser();

		Intent documentViewActivity = new Intent(MainActivity.this, DocumentActivity.class);

		documentViewActivity.putExtra(DocumentActivity.EXTRA_CURRENT_USER, currentlyActiveUser);
		documentViewActivity.putExtra(DocumentActivity.EXTRA_DOCUMENT_TYPE,
		        DocumentType.NEW_DOCUMENT);
		documentViewActivity.putExtra(DocumentActivity.EXTRA_FORM_DB_ID, formId);

		startActivity(documentViewActivity);

	}

	private void navigateToOpenExistingDocumentView(String documentId) {

		User currentlyActiveUser = mainWorkerFragment.getCurrentlyActiveUser();

		Intent documentViewActivity = new Intent(MainActivity.this, DocumentActivity.class);

		documentViewActivity.putExtra(DocumentActivity.EXTRA_CURRENT_USER, currentlyActiveUser);
		documentViewActivity.putExtra(DocumentActivity.EXTRA_DOCUMENT_TYPE,
		        DocumentType.EXISTING_DOCUMENT);
		documentViewActivity.putExtra(DocumentActivity.EXTRA_DOCUMENT_DB_ID, documentId);

		startActivity(documentViewActivity);

	}

	private void navigateToOpenOutgoingDocumentView(String outgoingDocumentDbId) {

		User currentlyActiveUser = mainWorkerFragment.getCurrentlyActiveUser();

		Intent documentViewActivity = new Intent(MainActivity.this, DocumentActivity.class);

		documentViewActivity.putExtra(DocumentActivity.EXTRA_CURRENT_USER, currentlyActiveUser);
		documentViewActivity.putExtra(DocumentActivity.EXTRA_DOCUMENT_TYPE,
		        DocumentType.OUTGOING_DOCUMENT);
		documentViewActivity.putExtra(DocumentActivity.EXTRA_DOCUMENT_DB_ID, outgoingDocumentDbId);

		startActivity(documentViewActivity);

	}

	private void notifyUserAboutSession(Mode mode) {

		if (mainViewFragment != null) {
			if (mode == Mode.OFFLINE) {
				mainViewFragment.notifyOfflineMode();
			} else {
				mainViewFragment.notifyOnlineMode();
			}
		}

	}

	private void notifyViewAboutNewDataFromServer(Document sourceDocument) {

		User currentlyActiveUser = mainWorkerFragment.getCurrentlyActiveUser();

		UserActionListViewItem userListActionListItem = mainWorkerFragment
		        .generateUserActionListItemFromDocument(sourceDocument, currentlyActiveUser);

		String currentUserWebId = Integer.toString(currentlyActiveUser.getWebId());

		// add the view data to their corresponding views
		mainViewFragment.addViewData(userListActionListItem, ViewDataType.ALL, ViewAddMode.APPEND);

		if (currentUserWebId.equals(sourceDocument.getRequestorWebId())) {
			mainViewFragment.addViewData(userListActionListItem, ViewDataType.MY_ACTIONS,
			        ViewAddMode.APPEND);
		}

		if (currentUserWebId.equals(sourceDocument.getProcessorWebId())) {
			mainViewFragment.addViewData(userListActionListItem, ViewDataType.MY_WORK,
			        ViewAddMode.APPEND);
		}

		if (sourceDocument.isStarred()) {
			mainViewFragment.addViewData(userListActionListItem, ViewDataType.STARRED,
			        ViewAddMode.APPEND);
		} else {
			refreshStarredDocumentsViewData("0");
		}

	}

	// }}

	// ========================================================================
	// {{ View Implementation Methods

	@Override
	public void onFormFilterChanged(FormStub stub) {
		// TODO: ask the main worker fragment for the appropriate data and then
		// update the main view fragment

		refreshDocumentsViewData(stub.webId);
	}

	@Override
	public void onRetryConnectionRequested() {

		if (!sessionManager.isCurrentlyCheckingServerConnection()) {
			sessionManager
			        .checkConnectionToServer(new CallbackCommand<SessionManager.CheckConnectionResult>() {

				        @Override
				        public void execute(CheckConnectionResult result) {
					        if (result.connectionStatus == HttpCommunicator.STATUS_CONNECTED) {
						        mainViewFragment.notifyOnlineMode();
					        } else {
						        mainViewFragment.notifyOfflineMode();
					        }
				        }
			        });

			mainViewFragment.dismissConnectionModeNotification();
			Toast.makeText(MainActivity.this, "Retrying connection", Toast.LENGTH_SHORT).show();
		} else {
			Toast.makeText(MainActivity.this, "Please wait", Toast.LENGTH_SHORT).show();
		}

	}

	@Override
	public void onRefreshDocumentsRequested() {

		if (fullUpdateCurrentlyRunning) {
			FLLogger.d(TAG, "full update currently running");
		} else {
			FLLogger.d(TAG, "full update not yet running");
		}

		if (fullUpdateCurrentlyRunning) {
			Toast.makeText(
			        MainActivity.this,
			        "Full update is still currently on going, the application will update the view for every new document.",
			        Toast.LENGTH_LONG).show();
		} else {

			if (sessionManager.getMode() == Mode.OFFLINE
			        && !sessionManager.isCurrentlyCheckingServerConnection()) {
				// check connection then start server if connected
				sessionManager
				        .checkConnectionToServer(new CallbackCommand<SessionManager.CheckConnectionResult>() {

					        @Override
					        public void execute(CheckConnectionResult result) {
						        if (result.connectionStatus == HttpCommunicator.STATUS_CONNECTED) {
							        startFullUpdateService();
						        } else {
							        Toast.makeText(MainActivity.this, "No connection to server.",
							                Toast.LENGTH_LONG).show();

							        mainViewFragment.stopDocumentsUpdateSwipeRefresh();
						        }
					        }
				        });

			} else {
				startFullUpdateService();
				FLLogger.d(TAG, "FullUpdateService is started via refresh");
			}

		}

	}

	@Override
	public void onRefreshOutboxDocumentsRequested() {
		refreshOutboxViewData();
		mainViewFragment.stopOutboxUpdateSwipeRefesh();
	}

	@Override
	public void onOpenDocumentCommand(String documentDbId) {

		navigateToOpenExistingDocumentView(documentDbId);

	}

	@Override
	public void onOpenOutboxDocumentCommand(String documentDBId, boolean isOutgoing) {
		if (isOutgoing) {
			navigateToOpenOutgoingDocumentView(documentDBId);
		} else {
			navigateToOpenExistingDocumentView(documentDBId);
		}

	}

	@Override
	public void onOpenDocumentCommentsCommand(UserActionListViewItem userActionListViewItem) {

		User currentUser = mainWorkerFragment.getCurrentlyActiveUser();

		String formId = userActionListViewItem.formWebId;
		String documentId = userActionListViewItem.documentWebId;

		Intent intent = new Intent(MainActivity.this, CommentsActivity.class);
		intent.putExtra(CommentsActivity.EXTRA_CURRENT_USER, currentUser);
		intent.putExtra(CommentsActivity.EXTRA_CURRENT_DOCUMENT_FORM_WEB_ID, formId);
		intent.putExtra(CommentsActivity.EXTRA_CURRENT_DOCUMENT_WEB_ID, documentId);
		startActivity(intent);
		this.overridePendingTransition(R.animator.push_up_in, R.animator.push_up_out);

	}

	@Override
	public void onMarkDocumentStarCommand(UserActionListViewItem userActionListViewItem,
	        DocumentStar newDocumentStar) {

		String formWebId = userActionListViewItem.formWebId;
		String documentDbId = userActionListViewItem.documentDbId;
		String documentWebId = userActionListViewItem.documentWebId;

		// mark the document as starred here in the device's local database
		mainWorkerFragment
		        .markDocumentStar(formWebId, documentWebId, documentDbId, newDocumentStar);

		// refresh view for all documents
		refreshDocumentsViewData("0");
		refreshOutboxViewData();

		// if the phone is online, mark the document in the server as well
		if (sessionManager.getMode() == Mode.ONLINE) {
			startMarkDocumentStarService(formWebId, documentWebId, newDocumentStar);
		}

	}

	@Override
	public void onOpenActionsDialogCommand(UserActionListViewItem userActionListViewItem) {

		String server = userActionListViewItem.server;
		String documentWebId = userActionListViewItem.documentWebId;
		String documentFormWebId = userActionListViewItem.formWebId;

		showActionsDialogForDocument(server, documentWebId, documentFormWebId);

	}

	// }}

	// ========================================================================
	// {{ Service Connections

	private ServiceConnection fullDataUpdateServiceConnection = new ServiceConnection() {

		@Override
		public void onServiceConnected(ComponentName componentName, IBinder binder) {
			FLLogger.d(TAG, "Service connected: " + componentName);

			FullDataUpdateService service = ((FullDataUpdateServiceBinder) binder).getService();
			service.startFullUpdate(fullDataUpdateListener);
			fullUpdateCurrentlyRunning = true;

		}

		@Override
		public void onServiceDisconnected(ComponentName componentName) {

			FLLogger.d(TAG, "Service disconnected: " + componentName);
			fullUpdateServiceBound = false;
			fullUpdateCurrentlyRunning = false;
		}
	};

	// }}

	// ========================================================================
	// {{ Service Listeners & Broadcast Receivers

	private FullDataUpdateListener fullDataUpdateListener = new FullDataUpdateListener() {

		@Override
		public void onUserNeedsToReauthenticate() {
			FLLogger.d(TAG,
			        "FullDataUpdateService failed in authenticating the currently active user, a login is needed");

			fullUpdateCurrentlyRunning = false;
			returnToLoginActivity();
		}

		@Override
		public void onFullUpdateDone() {
			fullUpdateCurrentlyRunning = false;

			unbindServices();
			if (mainViewFragment != null) {
				mainViewFragment.stopDocumentsUpdateSwipeRefresh();
			}
		}
	};

	private BroadcastReceiver formsUpdateBroadcastReceiver = new BroadcastReceiver() {

		@Override
		public void onReceive(Context context, Intent intent) {

			String action = intent.getAction();

			if (FormsUpdateService.ACTION_ON_UPDATE_STARTED.equals(action)) {
				formsAreCurrentlyUpdating = true;
				mainViewFragment.displayLoadingMessage("Synching forms");
			}

			if (FormsUpdateService.ACTION_ON_UPDATE_DONE.equals(action)) {

				// update the form filter selection in the fragment
				List<FormStub> updatedFormFilterSelection = mainWorkerFragment.getAvailableForms();
				mainViewFragment.setFormFilterSelection(updatedFormFilterSelection);

				// update the forms available for the create document
				currentFormFilterSelection.clear();
				currentFormFilterSelection.addAll(updatedFormFilterSelection);

				formsAreCurrentlyUpdating = false;

				mainViewFragment.dismissLoadingMessage();

			}

			if (FormsUpdateService.ACTION_ON_UPDATE_ERROR.equals(action)) {
				// TODO: report error here, then update documents
				FLLogger.d(TAG, "forms updating encountered an error");
			}

		}

	};

	private BroadcastReceiver documentsUpdateBroadcastReceiver = new BroadcastReceiver() {

		@Override
		public void onReceive(Context context, Intent intent) {
			String action = intent.getAction();

			Bundle extras = intent.getExtras();

			mainViewFragment.dismissLoadingMessage();

			if (DocumentsUpdateService.ACTION_ON_UPDATE_STARTED.equals(action)) {
				FLLogger.d(TAG, "DocumentsUpdateService started");
				// documentsAreCurrentlyUpdating = true;

				User currentUser = mainWorkerFragment.getCurrentlyActiveUser();
				if (mainWorkerFragment.getAvailableDocumentCount(currentUser.getAccountServer()) <= 0) {
					mainViewFragment.displayLoadingMessage("Synching documents");
				}

			} else if (DocumentsUpdateService.ACTION_ON_UPDATE_DONE.equals(action)) {
				FLLogger.d(TAG, "DocumentsUpdateService done");
				// documentsAreCurrentlyUpdating = false;
			}

			if (DocumentsUpdateService.ACTION_ON_DOCUMENT_UPDATED.equals(action)) {

				User currentlyActiveUser = mainWorkerFragment.getCurrentlyActiveUser();

				String server = currentlyActiveUser.getAccountServer();
				String formWebId = extras
				        .getString(DocumentsUpdateService.EXTRA_UPDATED_DOCUMENT_FORM_WEB_ID);
				String documentWebId = extras
				        .getString(DocumentsUpdateService.EXTRA_UPDATED_DOCUMENT_WEB_ID);

				try {
					Document updatedDocument = mainWorkerFragment.getDocument(documentWebId,
					        formWebId, server);
					notifyViewAboutNewDataFromServer(updatedDocument);
				} catch (JSONException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}

			} else if (DocumentsUpdateService.ACTION_ON_DOCUMENT_UPDATE_ERROR.equals(action)) {

				DocumentStub errorDocumentStub = (DocumentStub) extras
				        .getSerializable(DocumentsUpdateService.EXTRA_DOCUMENT_STUB);
				Exception exception = (Exception) extras
				        .get(DocumentsUpdateService.EXTRA_DOCUMENT_UPDATE_ERROR);

				FLLogger.w(TAG, "error downloading document: " + errorDocumentStub.webId
				        + " error: " + exception.getMessage());

			}
		}

	};

	private BroadcastReceiver markDocumentStarBroadcastReceiver = new BroadcastReceiver() {

		@Override
		public void onReceive(Context context, Intent intent) {
			// sUpdateBroadcastReceiverFilter.addAction(MarkDocumentStarService.ACTION_ON_SERVICE_STARTED);
			// formsUpdateBroadcastReceiverFilter.addAction(MarkDocumentStarService.ACTION_ON_SERVICE_DONE);

			String action = intent.getAction();

			FLLogger.d(TAG, "OutgoingStarMarksSubmitService " + action);

			// if
			// (OutgoingStarMarksSubmitService.ACTION_ON_SERVICE_STARTED.equals(action)) {
			// FLLogger.d(TAG, "MarkDocumentStarService started");
			// } else if
			// (OutgoingStarMarksSubmitService.ACTION_ON_SERVICE_DONE.equals(action)) {
			//
			// Bundle extras = intent.getExtras();
			//
			// boolean markingSuccess = extras
			// .getBoolean(OutgoingStarMarksSubmitService.EXTRA_MARK_SUCCESSFUL);
			// String markedDocumentWebId = extras
			// .getString(OutgoingStarMarksSubmitService.EXTRA_DOCUMENT_WEB_ID);
			//
			// if (markingSuccess) {
			// DocumentStar newStar = (DocumentStar) extras
			// .getSerializable(OutgoingStarMarksSubmitService.EXTRA_NEW_DOCUMENT_MARK);
			//
			// if (newStar == DocumentStar.STARRED) {
			// mainViewFragment.markDocumentStarInAllViews(markedDocumentWebId, newStar);
			// } else {
			// mainViewFragment.markDocumentStarInAllViews(markedDocumentWebId, newStar);
			// }
			// } else {
			// String errorMessage = extras
			// .getString(OutgoingStarMarksSubmitService.EXTRA_MARK_ERROR_MESSAGE);
			// FLLogger.d(TAG, "There was an error trying to star your document: "
			// + errorMessage);
			// }
			//
			// } else {
			// FLLogger.d(TAG,
			// "markDocumentStarBroadcastReceiver received unhandled action: "
			// + action);
			// }

		}

	};

	private BroadcastReceiver outgoingDocumentActionSubmitBroadcastReceiver = new BroadcastReceiver() {

		@Override
		public void onReceive(Context context, Intent intent) {
			String action = intent.getAction();

			FLLogger.d(TAG, "Received broadcast for " + action);

			if (OutgoingDocumentActionSubmitService.ACTION_ON_SERVICE_STARTED_SUBMITTING
			        .equals(action)) {
				// notificationManager.notifyOutgoingDocuments();
			} else if (OutgoingDocumentActionSubmitService.ACTION_ON_SERVICE_DONE.equals(action)) {
				startDocumentsUpdateService();
			}

			// update the user view
			refreshOutboxViewData();

		}

	};

	private SessionManagerEventListener sessionManagerEventListener = new SessionManagerEventListener() {

		@Override
		public void onSessionLost() {
			FLLogger.w(TAG, "Unhandled session lost event");
		}

		@Override
		public void onModeChanged(Mode mode) {
			notifyUserAboutSession(mode);
		}
	};

	// }}

}
